#include <stdio.h>
#include "mutex_buffer_pool.h"


mutex_buffer_pool* new_mutex_buffer_pool
(
    unsigned int user_count, 
    size_t single_buffer_size, 
    unsigned int buffer_count
)
{
    unsigned int i, j;
    mutex_buffer_pool* pool = malloc(sizeof(mutex_buffer_pool));
    pool->buffer_array = malloc(sizeof(char*) * buffer_count);
    for(i = 0; i < buffer_count; i++)
    {
        pool->buffer_array[i] = malloc(single_buffer_size);
        if(pool->buffer_array[i] == NULL)
        {
            fprintf(stderr, "fatal error: no more memory in newing buffer[%u]", i);
            
            while(--i >= 0)
                free(pool->buffer_array[i]);
            
            exit(2);
        }
    }

    pool->real_size_array = malloc(sizeof(size_t) * buffer_count);
    pool->sem_array = malloc(sizeof(sem_t*) * buffer_count);
    for(i = 0; i < buffer_count; i++)
        pool->sem_array[i] = malloc(sizeof(sem_t) * user_count);
    
    pool->user_index_array = calloc(user_count, sizeof(unsigned int));

    pool->single_buffer_size = single_buffer_size;
    pool->buffer_count = buffer_count;
    pool->user_count = user_count;
    
    for(i = 0; i < buffer_count; i++)
    {
        sem_init(pool->sem_array[i], 0, 1);      // 第一个用户一开始可用缓冲格
        
        for(j = 1; j < user_count; j++)
            sem_init(pool->sem_array[i] + j, 0, 0);  // 其他用户要等待第一个用户释放缓冲格
    }

    return pool;
}

void destroy_mutex_buffer_pool(mutex_buffer_pool* pool)
{
    unsigned int i, j;
    for(i = 0; i < pool->buffer_count; i++)
        free(pool->buffer_array[i]);
    free(pool->buffer_array);

    free(pool->real_size_array);

    for(i = 0; i < pool->buffer_count; i++)
    {
        for(j = 0; j < pool->user_count; j++)
            sem_destroy(pool->sem_array[i] + j);
        free(pool->sem_array[i]);
    }
    free(pool->sem_array);

    free(pool->user_index_array);
    
    free(pool);
}

char* get_buffer(mutex_buffer_pool* pool, unsigned int user)
{
    char* buffer = NULL;
    unsigned int index = pool->user_index_array[user];
    sem_wait(pool->sem_array[index] + user);
    
    buffer = pool->buffer_array[index];
    return buffer;
}

/* 设置 user 当前所用缓冲区的实际数据大小 */
void assign_size(mutex_buffer_pool* pool, unsigned int user, size_t real_size)
{
    pool->real_size_array[
        pool->user_index_array[user]
    ] = real_size;
}

/* 获取 user 当前所用缓冲区的实际数据大小 */
size_t get_real_size(mutex_buffer_pool* pool, unsigned int user)
{
    return pool->real_size_array[
        pool->user_index_array[user]
    ];
}

void release_buffer(mutex_buffer_pool* pool, unsigned int user)
{
    unsigned int index = pool->user_index_array[user];
    unsigned int next_user = (user + 1) % pool->user_count;
    
    sem_post(pool->sem_array[index] + next_user);
    
    index++;
    index %= pool->buffer_count;
    pool->user_index_array[user] = index;
}